/* mmio_test.c - Test MMIO read latency of various network adaptors
 *
 * (C) 2005 by Robert Olsson & Harald Welte
 *
 * This program is licensed under GNU GPL Version 2 as published by
 * the Free Software Foundation.
 */

/*

   Part of it is that uncached accesses are plain slow.  An L2 miss is ~370
   cycles on my hardware (155ns*2.4 confirms that), but an uncached access
   to the same memory location is consistently ~490 cycles.

   And part of it seems to be the e1000.  Reading the device control register
   (E1000_CTRL, offset 0x00) is ~1700 cycles each, but reading the interrupt
   cause register (E1000_ICR, 0xc0) is ~2100 cycles each.  The interrupt mask
   register (E1000_IMS, 0x100) is also ~2100 cycles each.

*/


#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>


#define OUTER_LOOP_COUNT	128
#define INNER_LOOP_COUNT	65535


static int dev_mem_fd;

/* core */

static void test_backend(volatile void *x, unsigned long offset)
{
    int i;

    for (i = 0; i <= INNER_LOOP_COUNT; i++) 
        *((volatile unsigned long *)x + offset);
}

static int generic_test(unsigned long long regs, int reg_num, int start_reg)
{
    struct timespec start_time, end_time;
    unsigned long long startl, endl;
    volatile void *x;
    int h;
    int loop = 4;

    x = mmap(NULL, 4096, PROT_READ, MAP_SHARED, dev_mem_fd, regs);
    if (x == MAP_FAILED) {
        perror("mmap");
        return -1;
    }

    for (h = start_reg; h < start_reg + reg_num; h+=4) {
        fflush(stdout);
        for (int i = 0; i < loop; i++) {
            clock_gettime(CLOCK_MONOTONIC_RAW, & start_time);
            for (int j = 0; j <= OUTER_LOOP_COUNT; j++)
                test_backend(x, h);

            clock_gettime(CLOCK_MONOTONIC_RAW, & end_time);
            startl= (unsigned long long)(start_time.tv_sec)*1000000000 + (unsigned long long)(start_time.tv_nsec);
            endl= (unsigned long long)(end_time.tv_sec)*1000000000 + (unsigned long long)(end_time.tv_nsec);

            printf("%lld ",(endl-startl)/(OUTER_LOOP_COUNT * INNER_LOOP_COUNT));
            fflush(stdout);
        }
    }
    munmap((void *)x, 4096);

    return 0;
}

#define BA 0xafd80000

int main(int argc, char **argv)
{

    dev_mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (dev_mem_fd < 0) {
        perror("open");
        fprintf(stderr, "you must run this as root, sorry\n");
        exit(-1);
    }
    generic_test(BA, 1, 0);
    exit(0);
}
